package cn.xiaolang.thread;

import lombok.NonNull;
import lombok.extern.slf4j.Slf4j;

import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.stream.Collectors;

/**
 * 王者荣耀的组队游戏的demo
 *
 * @author DeanKano
 * @date 2018年3月17日
 */
@Slf4j
public class GameDemo {

    /**
     * 6人，10s 开局，overGame;
     * 示例：
     * round1 （有玩家未确认，10s后组队失败）
     * round2&round3  (玩家正常确认，正常在房间运行，游戏结束)
     */
    public static void main(String[] args) throws InterruptedException {

        AtomicInteger teamNO = new AtomicInteger(1000);
        List<String> players = Arrays.asList("玩家A", "玩家B", "玩家C", "玩家D", "玩家E", "玩家F");

        GameTeamThread round1 = new GameTeamThread(players, teamNO.incrementAndGet());
        round1.readyOne("玩家A");
        round1.readyOne("玩家C");
        round1.readyOne("玩家E");
        round1.readyOne("玩家F");
        Thread.sleep(4000);
        round1.readyOne("玩家D");

        GameTeamThread round2 = new GameTeamThread(players, teamNO.incrementAndGet());
        GameTeamThread round3 = new GameTeamThread(players, teamNO.incrementAndGet());
        // 玩家正常确认
        players.stream().forEach(round2::readyOne);
        players.stream().forEach(round3::readyOne);
        Thread.sleep(12000);
        round2.gameRunner.overGame();
        Thread.sleep(3000);
        round3.gameRunner.overGame();
    }


    /**
     * 获取当前距离startTime的秒数
     *
     * @return
     */
    private static long getDiffSecondsFrom(long startTime) {
        return TimeUnit.SECONDS.convert(System.currentTimeMillis() - startTime, TimeUnit.MILLISECONDS);
    }

    /**
     * 组队线程
     *
     * @author DeanKano
     * @date 2018年3月17日
     */
    public static class GameTeamThread extends Thread {

        //倒计时(s)
        private static final long MAX_COUNT_DOWN_SECONDS = 10;
        // 是否可以组队
        private volatile boolean allowTeam = true;
        // 是否组队成功
        private volatile boolean teamResult = false;

        private Map<String, Boolean> playersMap;

        private long startTime = System.currentTimeMillis();

        private GamePlayingThread gameRunner;

        private String name;

        private static final String TEAM_NAME_PREFIX = "T-";

        private AtomicInteger roomNum = new AtomicInteger(10000);

        /**
         * @param players 玩家
         * @param teamNO 组队编号
         */
        private GameTeamThread(@NonNull List<String> players, int teamNO) {
            this.name = TEAM_NAME_PREFIX + teamNO;
            this.playersMap = players.stream().collect(Collectors.toMap(a -> a, b -> false));
            this.start();
        }

        /**
         * 玩家确认
         */
        private void readyOne(String playerName) {
            playersMap.computeIfPresent(playerName, (k, v) -> true);
        }

        private boolean isAllReady() {
            long countDownSeconds = getDiffSecondsFrom(this.startTime);
            // 未超时并且都确认啦
            return countDownSeconds <= MAX_COUNT_DOWN_SECONDS && !playersMap.values().contains(false);
        }

        @Override
        public void run() {
            try {
                while (allowTeam) {
                    sleep(2000);
                    if (teamResult) {
                        log.info("{}：组队成功，开始游戏", this.name);
                        this.gameRunner = new GamePlayingThread(this.name, roomNum.incrementAndGet());
                        gameRunner.start();
                        break;
                    }
                    long countDownSeconds = getDiffSecondsFrom(this.startTime);
                    if (countDownSeconds > MAX_COUNT_DOWN_SECONDS) {
                        this.allowTeam = false;
                        log.info("{}：组队失败，{}(s)超时，有玩家未确认", this.name, MAX_COUNT_DOWN_SECONDS);
                        break;
                    }
                    if (isAllReady()) {
                        this.teamResult = true;
                    }

                    log.info("{} 用时：{}s,->组队信息：{}", this.name, countDownSeconds, this.playersMap.toString());
                }
            } catch (InterruptedException ex) {
                log.error("{}:组队进程中断!,exception:{}", this.name, ex);
            }
        }


        public void stopTeam() {
            this.allowTeam = false;
            synchronized (this) {
                notifyAll();
            }
        }
    }

    /**
     * 游戏线程
     *
     * @author DeanKano
     * @date 2018年3月17日
     */
    public static class GamePlayingThread extends Thread {

        // volatile并发可见性，保证同一时刻只有一个线程修改该变量
        private volatile boolean overGame;

        private long startTime = System.currentTimeMillis();

        private String name;

        private static final String ROOM_NAME_PREFIX = "R-";

        private GamePlayingThread(String teamName, int roomNum) {
            this.name = teamName + "(" + ROOM_NAME_PREFIX + roomNum + ")";
        }

        @Override
        public void run() {
            try {
                while (!overGame) {
                    synchronized (this) {
                        wait(2000);
                        log.info("{}：游戏 ing...", this.name);
                    }
                }
            } catch (InterruptedException ex) {
                log.error("{}:gameRunner interrupted !,exception:{}", this.name, ex);
            } finally {
                long recordTimes = getDiffSecondsFrom(this.startTime);
                log.info("{}::gameRunner over !,用时：{}(s)", this.name, recordTimes);
            }
        }

        public void overGame() {
            this.overGame = true;
            synchronized (this) {
                notifyAll();
            }
        }
    }

}